Kalıtıma Giriş
Java'da kalıtım ve kompozisyon, nesne yönelimli programlama prensiplerinden ikisidir ve sınıfların birbirine olan ilişkisini belirlemek için kullanılır. Her birinin farklı avantajları ve kullanım durumları vardır.
Kalıtım, bir sınıfın, başka bir sınıfın özelliklerini ve davranışlarını miras alarak genişletmesini sağlar. Bu, kodun tekrarını önler ve sınıflar arasındaki bağımlılıkları azaltır.
Kalıtım kullanmanın önemli avantajlarından biri, kodun yeniden kullanılabilirliğini artırmasıdır.
Kalıtımın bir diğer faydası, bir sınıfın, miras aldığı sınıfların yöntemlerini ve özelliklerini kullanarak karmaşık davranışlar gerçekleştirebilmesidir. Ancak, kalıtım kullanırken, alt sınıfın üst sınıftan miras aldığı özellikleri ve davranışları kullanabilmesi için üst sınıfın iyi tasarlanmış ve stabil olması gerekir.
Kompozisyon ise, bir sınıfın, diğer sınıfları birleştirerek yeni bir sınıf oluşturmasını sağlar. Bu, kodun modülerliğini artırır, bağımlılıkları azaltır ve karmaşık nesnelerin oluşturulmasını sağlar.
Genel olarak, kalıtım, nesneler arasında bir “is-a” ilişkisi varsa, kompozisyon ise bir “has-a” ilişkisi varsa kullanılır. Örneğin, bir Araba sınıfı, bir Motor sınıfından türetilmiş olabilir, çünkü bir arabaya bir motorun ait olması "is-a" ilişkisini gösterir.
Kompozisyon
Java'da kompozisyon, bir sınıfın, diğer sınıfların örneklerini kullanarak bir nesne oluşturmasını sağlayan bir nesne yönelimli programlama prensibidir. Bu, bir sınıfın karmaşık özelliklerini ve davranışlarını, diğer sınıflara bölerek daha modüler ve okunaklı bir kod oluşturmasına yardımcı olur.
Örnek olarak, bir "Araba" sınıfı oluşturmak istediğimizi düşünelim. Araba, bir motor, bir şanzıman ve bir dizi lastik gibi birçok bileşen içerir. Bu bileşenlerin hepsi ayrı sınıflarda modellenebilir ve Araba sınıfı bu bileşenleri kullanarak oluşturulabilir.
Bunu gerçekleştirmek için, Araba sınıfında diğer sınıfların örneklerini oluşturabilir ve onları kullanabiliriz. Aşağıdaki örnek, Araba sınıfının, bir Motor ve bir Şanzıman sınıfı örneği içerdiğini ve ayrıca dört adet Lastik örneğine sahip olduğunu göstermektedir:
public class Araba {
private Motor motor;
private Şanzıman şanzıman;
private Lastik[] lastikler;
public Araba() {
motor = new Motor();
şanzıman = new Şanzıman();
lastikler = new Lastik[4];
for(int i=0; i<4; i++) {
lastikler[i] = new Lastik();
}
}
// diğer metodlar
}
public class Motor {
// Motor sınıfı özellikleri ve davranışları
}
public class Şanzıman {
// Şanzıman sınıfı özellikleri ve davranışları
}
public class Lastik {
// Lastik sınıfı özellikleri ve davranışları
}
Bu örnekte, Araba sınıfının motor, şanzıman ve lastikler adında üç özelliği vardır. Araba sınıfının yapıcısı, motor ve şanzıman örneklerini oluştururken, lastik örnekleri için bir dizi oluşturur ve her bir öğe için ayrı ayrı Lastik sınıfı örneği oluşturur.
Bu sayede, Araba sınıfının kodu daha modüler ve okunaklı hale gelir, ayrıca Araba sınıfının değişikliklerinden diğer bileşenler etkilenmez.
Kalıtım
Java'da kalıtım (inheritance), bir sınıfın başka bir sınıfın özelliklerini ve davranışlarını miras almasıdır. Kalıtım sayesinde bir sınıf, var olan bir sınıftan tüm özelliklerini ve davranışlarını alarak yeni bir sınıf oluşturabilir. Kalıtım, Java'da nesne yönelimli programlama (Object-Oriented Programming) temel prensiplerinden biridir ve programlamada kod tekrarını azaltarak kodun yeniden kullanılabilirliğini arttırır.
Kalıtımın kullanımı, "extends" anahtar kelimesi ile gösterilir. Örneğin, bir Person sınıfı oluşturabilir ve bu sınıfta insanlar için genel özellikler ve davranışlar tanımlayabiliriz. Daha sonra, bu sınıfı temel alarak Student ve Teacher sınıflarını oluşturabiliriz. Bu sınıflar, Person sınıfının tüm özelliklerini ve davranışlarını kalıtım yoluyla almış olurlar.
Aşağıda örnek bir kod gösterimi bulunmaktadır:
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("Person is eating.");
}
public void sleep() {
System.out.println("Person is sleeping.");
}
}
class Student extends Person {
private int studentId;
public Student(String name, int age, int studentId) {
super(name, age);
this.studentId = studentId;
}
public void study() {
System.out.println("Student is studying.");
}
}
class Teacher extends Person {
private String subject;
public Teacher(String name, int age, String subject) {
super(name, age);
this.subject = subject;
}
public void teach() {
System.out.println("Teacher is teaching " + this.subject + ".");
}
}
Yukarıdaki örnekte, Person sınıfı insanların temel özelliklerini ve davranışlarını tanımlar. Student sınıfı, Person sınıfından kalıtım yoluyla tüm özelliklerini ve davranışlarını almıştır ve ek olarak studentId özelliğini de içermektedir.
Student ve Teacher sınıfları, Person sınıfından kalıtım yoluyla tüm özelliklerini ve davranışlarını almış ve kendi özelliklerini de eklemişlerdir. Bu sayede, kod tekrarından kaçınılmış ve yeniden kullanılabilir bir kod yazılmıştır.
Overriding
Overriding, Java programlama dilinde bir alt sınıfın üst sınıfın metodunu aynı isim ve imza ile yeniden tanımlamasıdır. Bu sayede alt sınıf, üst sınıfın metodunu kendi ihtiyacına göre değiştirebilir ve aynı isim ve imza ile kullanabilir.
Kalıtım kullanıldığında, bir alt sınıfın üst sınıfın metodlarını kullanabileceği gibi, bu metodları override ederek kendine özgü metodlar da tanımlayabilir.
Aşağıda, bir örnek kod gösterimi verilmiştir:
class Animal {
public void makeSound() {
System.out.println("The animal makes a sound");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
animal.makeSound(); // The animal makes a sound
Cat cat = new Cat();
cat.makeSound(); // Meow
}
}
Yukarıdaki örnekte, Animal sınıfı "makeSound" metodunu tanımlamıştır. Cat sınıfı, Animal sınıfından kalıtım yoluyla tüm özelliklerini ve davranışlarını almıştır. Ancak, Cat sınıfı "makeSound" metodunu override ederek, kendi özel "Meow" metodu tanımlamıştır.
Overriding, sınıflar arasında birbirlerinden farklı özellikler ve davranışlar eklemek için önemli bir araçtır. Yanlış bir kullanımla karşılaşılabilir.
Overloading
Overloading, aynı isimli fakat farklı parametrelerle birlikte farklı şekillerde kullanılan metodların tanımlanmasıdır. Bu sayede, aynı işlevi farklı parametrelerle çağırarak kodun daha modüler ve kolay anlaşılabilir olmasını sağlar.
Java'da kalıtım konusu ile düşünürsek, bir sınıfın başka bir sınıftan kalıtım alması durumunda, kalıtım aldığı sınıfın metodlarına erişebilir ve bu metodları istediği şekilde kullanabilir. Ancak, kalıtım alan sınıf aynı isimli bir metodunu tanımlarsa, bu durumda metot overloading özelliği devreye girer.
Örneğin, bir Hayvan sınıfı tanımlayalım ve bu sınıfın konuşma özelliği olsun. Aşağıdaki şekilde tanımlanabilir:
public class Hayvan {
public void konus() {
System.out.println("Hayvan konuşuyor.");
}
}
Ardından, bu sınıftan kalıtım alan bir Köpek sınıfı tanımlayalım ve Köpek sınıfı da konuşma özelliğine sahip olsun.
public class Kopek extends Hayvan {
@Override
public void konus() {
System.out.println("Hav! Hav!");
}
public void konus(String mesaj) {
System.out.println(mesaj);
}
}
Bu örnekte, Kopek sınıfı, Hayvan sınıfından kalıtım alarak konuşma özelliğini miras almıştır.
Köpek sınıfı Hayvan sınıfının konuşma metodunu overloading yaparak kendi konuşma metodunu tanımlamıştır.
Upcasting
Java'da upcasting, bir alt sınıf nesnesinin bir üst sınıf referansı ile işaret edilmesi anlamına gelir. Bu, bir nesnenin, o nesneyi oluşturan sınıfın yanı sıra, üst sınıfların tüm referanslarını da alabileceği anlamına gelir.
Hayvan hayvan = new Kopek();
Bu örnekte, bir Köpek nesnesi oluşturulmuş ve Hayvan sınıfı referansı ile işaret edilmiştir. Bu sayede, Köpek nesnesinin tüm özellikleri kullanılamasa da, Hayvan sınıfının sahip olduğu tüm özelliklere erişilebilir.
Downcasting
Java'da downcasting, bir üst sınıf referansı ile işaret edilen bir nesnenin, alt sınıf referansı ile işaret edilmesi işlemidir. Bu, upcasting işleminin tam tersidir ve bazen bir üst sınıf referansı ile işaret edilen nesneyi, alt sınıfın özel bir özelliğine erişmek için kullanılır.
Aşağıdaki örnekte, instanceof anahtar kelimesi kullanılarak downcasting kontrol edilir:
Hayvan hayvan = new Kopek();
if (hayvan instanceof Kopek) {
Kopek kopek = (Kopek) hayvan;
kopek.havla();
}
Downcasting işlemi yanlış bir sınıf referansı ile yapılırsa, "ClassCastException" hatası oluşabilir. Bu nedenle dikkatli olunmalıdır.
Final Özelliği
Java'da final anahtar kelimesi, değişmez bir özellik tanımlamak için kullanılır. Bu anahtar kelime, değişken, metod ve sınıf tanımlamalarında kullanılabilir.
Değişkenlerde final anahtar kelimesi: Bir değişkenin "final" olarak tanımlanması, o değişkenin bir kez değer atanmasına izin verir ve daha sonra değeri değiştirilemez. Örneğin:
final int PI = 3.14;
Metodlarda final anahtar kelimesi: Final anahtar kelimesi, bir metodun alt sınıflar tarafından yeniden tanımlanmasını engeller. Örneğin:
public final void setYas(int yas) {
this.yas = yas;
}Sınıflarda final anahtar kelimesi: Final anahtar kelimesi, bir sınıfın alt sınıflarının oluşturulmasını engeller. Örneğin:
public final class Circle {
//...
}
Final anahtar kelimesi, Java kodunun güvenliğini artırır ve hataları önler. Bantlama ve aşırı yükleme gibi kavramlar, Java'nın esnekliğini ve okunabilirliğini artırır.